/********************************************************************************************/
/* Fullhan Technology (Shanghai) Co., Ltd.                                                  */
/* Fullhan Proprietary and Confidential                                                     */
/* Copyright (c) 2017 Fullhan Technology Group Limited and its affiliated companies         */
/********************************************************************************************/
#include "drv_cesa_if.h"
#include "hw_cesa_if.h"
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/dma-mapping.h>


void drv_cesa_aux_init(FH_CESA_AUX_S *p_aux)
{
	sema_init(&p_aux->sem_ades, 1);
	sema_init(&p_aux->sem_sha, 1);
	sema_init(&p_aux->sem_rsa, 1);

	p_aux->keep_ades = 0;

	init_completion(&p_aux->completion_ades);

	p_aux->ker_data_addr = kmalloc(ADES_MAX_BLOCK, GFP_KERNEL);
	if (p_aux->ker_data_addr) {
		p_aux->phy_data_addr = dma_map_single(p_aux->dev, p_aux->ker_data_addr, ADES_MAX_BLOCK, DMA_BIDIRECTIONAL);
		if (p_aux->phy_data_addr == 0)
			kfree(p_aux->ker_data_addr);
	}
}

void drv_cesa_aux_deinit(FH_CESA_AUX_S *p_aux)
{
	p_aux->keep_ades = 0;

	if (p_aux->phy_data_addr) {
		dma_unmap_single(p_aux->dev, p_aux->phy_data_addr, ADES_MAX_BLOCK, DMA_BIDIRECTIONAL);
		kfree(p_aux->ker_data_addr);
	}
}

uint32_t drv_cesa_ades_phy_data_addr(uint32_t session)
{
	FH_CESA_AUX_S *p_aux = (FH_CESA_AUX_S *)((HW_CESA_ADES_CTRL_S *)session)->priv;

	return p_aux->phy_data_addr;
}

int32_t drv_cesa_ades_dma_sync_single_for_device(uint32_t session, uint32_t user_src_addr, uint32_t length)
{
	FH_CESA_AUX_S *p_aux = (FH_CESA_AUX_S *)((HW_CESA_ADES_CTRL_S *)session)->priv;

	if (copy_from_user(p_aux->ker_data_addr,
			(void __user *)user_src_addr,
			length)) {
		return -1003;
	}
	dma_sync_single_for_device(p_aux->dev, p_aux->phy_data_addr, length, DMA_BIDIRECTIONAL);

	return 0;
}

int32_t drv_cesa_ades_dma_sync_single_for_cpu(uint32_t session, uint32_t user_dst_addr, uint32_t length)
{
	FH_CESA_AUX_S *p_aux = (FH_CESA_AUX_S *)((HW_CESA_ADES_CTRL_S *)session)->priv;

	dma_sync_single_for_cpu(p_aux->dev, p_aux->phy_data_addr, length, DMA_BIDIRECTIONAL);
	if (copy_to_user((void __user *)user_dst_addr,
			p_aux->ker_data_addr,
			length)) {
		return -1006;
	}

	return 0;
}

int32_t drv_cesa_ades_dma_sync_single_for_device_sg(uint32_t session,
	struct scatterlist *sgl, uint32_t proc_len, uint32_t skip)
{
	FH_CESA_AUX_S *p_aux =
		(FH_CESA_AUX_S *)((HW_CESA_ADES_CTRL_S *)session)->priv;
	uint32_t copy_len;

	copy_len = sg_pcopy_to_buffer(sgl,
		sg_nents(sgl), p_aux->ker_data_addr, proc_len, skip);
	if (copy_len != proc_len) {
		pr_err("%s sg_pcopy_to_buffer remaining len %d\n",
			__func__, proc_len - copy_len);
		return -1003;
	}
	dma_sync_single_for_device(p_aux->dev,
		p_aux->phy_data_addr, proc_len, DMA_BIDIRECTIONAL);

	return 0;
}

int32_t drv_cesa_ades_dma_sync_single_for_cpu_sg(uint32_t session,
	struct scatterlist *sgl, uint32_t proc_len, uint32_t skip)
{
	FH_CESA_AUX_S *p_aux =
		(FH_CESA_AUX_S *)((HW_CESA_ADES_CTRL_S *)session)->priv;
	uint32_t copy_len;

	dma_sync_single_for_cpu(p_aux->dev,
		p_aux->phy_data_addr, proc_len, DMA_BIDIRECTIONAL);

	copy_len = sg_pcopy_from_buffer(sgl,
		sg_nents(sgl), p_aux->ker_data_addr, proc_len, skip);
	if (copy_len != proc_len) {
		pr_err("%s sg_pcopy_from_buffer remaining len %d\n",
			__func__, proc_len - copy_len);
		return -1006;
	}

	return 0;
}

void drv_cesa_ades_lock(uint32_t session)
{
	FH_CESA_AUX_S *p_aux = (FH_CESA_AUX_S *)((HW_CESA_ADES_CTRL_S *)session)->priv;

	if (p_aux) {
		if (down_interruptible(&p_aux->sem_ades))
			printk("ades lock failed!\n");
	}
}

void drv_cesa_ades_unlock(uint32_t session)
{
	FH_CESA_AUX_S *p_aux = (FH_CESA_AUX_S *)((HW_CESA_ADES_CTRL_S *)session)->priv;

	if (p_aux)
		up(&p_aux->sem_ades);
}



bool drv_cesa_check_keep(uint32_t session, uint32_t same_diff_both)
{
	FH_CESA_AUX_S *p_aux = (FH_CESA_AUX_S *)((HW_CESA_ADES_CTRL_S *)session)->priv;

	if (p_aux) {
		if (same_diff_both == 0) {
			if (p_aux->keep_ades == session) {
				p_aux->keep_ades = 0;
				return true;
			}
		}

		if (same_diff_both == 1) {
			if (p_aux->keep_ades != session) {
				p_aux->keep_ades = session;
				return true;
			}
		}

		if (same_diff_both == 2) {
			if (p_aux->keep_ades == session) {
				p_aux->keep_ades = 0;
				return true;
			}
			else {
				p_aux->keep_ades = session;
				return true;
			}
		}

		return false;
	} else {
		return true;
	}
}

void drv_cesa_ades_send_complete(void *cesa_aux)
{
	FH_CESA_AUX_S *p_aux = (FH_CESA_AUX_S *)cesa_aux;

	complete(&p_aux->completion_ades);
}

int32_t drv_cesa_ades_wait_for_complete(uint32_t session)
{
	FH_CESA_AUX_S *p_aux = (FH_CESA_AUX_S *)((HW_CESA_ADES_CTRL_S *)session)->priv;

	/* wait completion timeout about 2 seconds */
	if (wait_for_completion_timeout(&p_aux->completion_ades, 200) == 0)
		return CESA_ADES_DMA_DONE_TIMEOUT;

	return hw_ades_ext_dma_done((HW_CESA_ADES_CTRL_S *)session);
}

void drv_cipher_data_print(int8_t *string, uint8_t *data, uint32_t length, uint8_t align)
{
	uint32_t i;

	if (string != NULL)
	{
		printk("%s \n", string);
	}

	if (data != NULL)
	{
		for(i = 0; i < length; i++)
		{
			printk("%02x ", data[i]);
			if(((i+1)%align) == 0)//(0 == ((i+1) & (align-1)))
			{
				printk("\n");
			}
		}
		printk("\n");
	}
}

